import {Map, View} from 'ol'
// 图上图标相关
import Feature from 'ol/Feature'
import {Point, LineString, MultiPolygon, Circle, Polygon} from 'ol/geom'
import {
  Vector as VectorSource,
  TileWMS,
  ImageWMS,
  TileImage,
  XYZ as XYZSsource,
  Cluster
} from 'ol/source'
// 用来添加相关文字描述的
import {
  Text,
  Style,
  Fill,
  Icon,
  Circle as CircleStyle,
  Stroke
} from 'ol/style'
import TileGrid from 'ol/tilegrid/TileGrid'
// 控制层
import {Zoom, ZoomSlider, MousePosition} from 'ol/control'
import * as olProj from 'ol/proj'
import {get as olProjGet} from 'ol/proj'
import {defaults} from 'ol/interaction'

import {defsCGCS2000, defsBD09, defsWGS84} from '../projection'
import {
  Image as ImageLayer,
  Tile as TileLayer,
  Vector as VectorLayer,
  Heatmap as HeatmapLayer
} from 'ol/layer'

import {GeoJSON} from 'ol/format'
// 经纬度转换
import {createStringXY} from 'ol/coordinate'
// 4
import {transform, transformExtent} from 'ol/proj'
import Overlay from 'ol/Overlay'

//百度地图处理
import {createBDLayerSource, createGisSource} from './LayerSource'

//6
import {boundingExtent as BoundingExtent} from 'ol/extent'

class Dbmap {
  constructor(mapid, option) {
    // 投影转换
    //defsCGCS2000()
    //百度坐标
    defsBD09()

    // defsWGS84()

    this.map = new Map({
      target: mapid,
      controls: [],
      interactions: defaults({
        doubleClickZoom: false
      })
    })
    this.add = false
    if (process.env.VUE_APP_MAP_PARAMS) {
      option.params = process.env.VUE_APP_MAP_PARAMS
    }
    this.option = option
    this.init()
  }

  // 初始化地图控件
  init() {
    let _this = this
    let options = this.option

    // 加载控件
    this.loadControls(options)

    // 初始化图层
    this.loadView(options)
    // 添加图层
    if (options.layerList) {
      options.layerList.forEach(function (item) {
        _this.loadLayer(item)
      })
      // 添加点击事件
    }
  }

  /**
   * 加载视图
   * @param options 地图参数
   */
  loadView(options) {
    // 加载视图参数
    var viewParams = JSON.parse(options.params)
    // var viewParams = {
    //   'projection': 'EPSG:4326',
    //   'center': [
    //     91.177308, 29.653461
    //   ],
    //   'minZoom': 6,
    //   'maxZoom': 16,
    //   'zoom': 7
    // }

    // 中心点投影转换
    if (viewParams.center) {
      viewParams.center = this.transform(...viewParams.center)
    }

    // 显示范围转换
    if (viewParams.extent) {
      let extent = viewParams.extent
      //最小经纬度
      let minLonlat = this.transform(extent[0], extent[1])
      //最大经纬度
      let maxLonlat = this.transform(extent[2], extent[3])
      viewParams.extent = [...minLonlat, ...maxLonlat]
    }
    // the style to use for the lines, optional.
    this.map.setView(new View(viewParams))
  }

  /**
   * 加载图层
   * @param options 图层参数
   */
  loadLayer(options) {
    // 参数
    var params = JSON.parse(options.params)
    // JSON.parse(options.params)
    let coordinatecode = options.coordinatecode
    // 显示范围投影转换
    if (params.extent7) {
      params.extent = olProj.transformExtent(
        params.extent,
        coordinatecode,
        this.map.getView().getProjection()
      )
    }
    var tileParams = params

    var sourceParams = params.source
    delete tileParams['source']
    // 替换url和layers参数
    if (options.url) {
      sourceParams.url = options.url
      //  sourceParams.crossOrigin = 'anonymous'
    }
    //支持跨域
    if (options && options.crossOrigin) {
      sourceParams.crossOrigin = options.crossOrigin || ''
    }
    if (options.layers) {
      if (!sourceParams.params) {
        sourceParams.params = {}
      }
      sourceParams.params.layers = options.layers
    }
    // 图层创建

    let imgtype = options.layertype
    var layer = null
    // 图层
    switch (imgtype) {
      case 'Tile':
        layer = new TileLayer(tileParams)
        break
      case 'Image':
        layer = new ImageLayer(tileParams)
        break
      case 'Vector':
        //加载矢量
        layer = new VectorLayer({
          ...tileParams,
          style: function (features) {
            let src = features.get('src')

            let attr = features.get('attr')
            let icon = null
            if (src) {
              icon = new Icon({
                src: src
              })
            } else {
              icon = new CircleStyle({
                radius: 3,
                fill: new Fill({
                  color: 'red'
                })
              })
            }

            return new Style({
              image: icon,
              text: new Text({
                text: attr ? attr.id : '',
                font: '12px sans-serif',
                offsetY: -10,
                fill: new Fill({
                  color: '#fff'
                })
              })
            })
          }
        })
        break
      default:
        return
    }
    // 源
    let sourcetype = options.sourcetype
    // source设置
    let source = null
    if (sourcetype === 'TileWMS') {
      source = new TileWMS(sourceParams)
    } else if (sourcetype === 'ImageWMS') {
      source = new ImageWMS(sourceParams)
    } else if (sourcetype === 'Point') {
      //矢量图层
      source = new VectorSource({
        features: []
      })
    } else if (sourcetype === 'BDXYZ') {
      // 加载百度地图，百度地图加载层级需要单独处理
      source = createBDLayerSource(sourceParams)
    } else if (sourcetype === 'CDSXYZ') {
      // 加载成都市地图
      source = createGisSource({
        ...options,
        theme: this.option.theme
      })
    } else {
      source = new XYZSsource(sourceParams)
    }
    // 图层编码
    layer.code = options.code
    layer.setSource(source)
    // layer.setVisible(true)
    this.map.addLayer(layer)
  }

  /**
   * grayscale灰度
   * sepia褐色（有种复古的旧照片感觉）
   * saturate饱和度
   * hue-rotate色相旋转
   * invert反色
   * opacity透明度
   * brightness亮度
   * contrast对比度
   * blur模糊
   * drop-shadow阴影
   */
  filter(type) {
    let data = ''
    switch (type) {
      case 'blue':
        data = "invert(100%)";
        break
      case 'black':
        data = "sepia(120%)";
        break
      case 'light':
        data = "saturate(100%)";
        break
      case 'aa':
        data = "grayscale(98%) invert(100%) sepia(20%) hue-rotate(180deg) saturate(1600%) ";
        break
      default:
        data = ''
        break
    }
    return data;
  }

  /**
   * 加载控件
   * @param options 地图参数
   */
  loadControls(options) {
    // 是否显示当前经纬度
    if (options.mousepositionshow) {
      this.map.addControl(getMousePosition(options.mousepositionlen, this.map))
    }

    // 是否显示缩放调整控件
    if (options.zoomshow) {
      this.map.addControl(new Zoom())
    }
    // 是否显示缩放滑块控件
    if (options.zoomslidershow) {
      this.map.addControl(new ZoomSlider())
    }
  }

  // 通过图层编码获取图层
  getLayer = function (code) {
    let layers = this.map.getLayers().getArray()
    for (let i = 0; i < layers.length; i++) {
      let item = layers[i]
      if (code === item.code || code === item.get('code')) {
        return item
      }
    }
  }

  // 清除一个图层上的点
  clearLayer(code) {
    let layer = this.getLayer(code)
    if (layer) {
      layer.getSource().clear()
      return layer
    }
  }

  clearLayers(layers = []) {
    for (const layerName of layers) {
      this.clearLayer(layerName)
    }
  }

  //创建一个热力图
  createHeatmapLayer(code, option) {
    let colors = ['#2200FF', '#16D9CC', '#4DEE12', '#E8D225', '#EF1616']
    let layer = new HeatmapLayer({
      source: new VectorSource(),
      blur: 15, //模糊大小(像素为单位)
      radius: 8, //半径大小默认为8(像素为单位)
      gradient: colors,
      ...option
    })
    if (code) {
      layer.code = code
    }
    // 把图层添加到地图
    this.map.addLayer(layer)
    return layer
  }

  //图层参数，图层编码
  createGeoJson(url, code, option = {}, formatOption) {
    let defaultOption = {
      source: new VectorSource({
        url: url,
        format: new GeoJSON({
          ...formatOption
        })
      }),
      ...option
    }
    // 创建图层
    var layer = new VectorLayer(defaultOption)
    if (code) {
      layer.code = code
    }
    // 把图层添加到地图
    this.map.addLayer(layer)
    return layer
  }

  //XYZ瓦片
  createTileXYZ(option, code) {
    let layer = new TileLayer({
      source: new XYZSsource(option)
    })
    if (code) {
      layer.code = code
    }
    this.map.addLayer(layer)
  }

  /**
   * 创建图层并添加到Map(用于绘制点、线、面等)
   * @param option ol.layer.Vector的内容
   * @param code 图层编码
   */
  createVector(option, code) {
    // 默认Option
    var defaultOption = {
      source: new VectorSource({
        features: []
      }),
      ...option
    }
    // 继承属性

    // 创建图层
    var layer = new VectorLayer(defaultOption)
    if (code) {
      layer.code = code
    }

    // 把图层添加到地图
    this.map.addLayer(layer)
    return layer
  }

  /**
   * 创建一个聚合图层，聚合图层和其他矢量图cc
   * @param option ol.layer.Vector的内容
   * @param code 图层编码
   * @param cluster_opt
   */
  createClusterVector(option, code, cluster_opt) {
    // 默认Option
    var defaultOption = {
      source: new Cluster({
        source: new VectorSource({
          features: []
        }),
        distance: 100,
        ...cluster_opt
        // minDistance: 15, // 聚合点与点之间的最小距离
      }),
      ...option
    }

    // 创建图层
    var layer = new VectorLayer(defaultOption)
    if (code) {
      layer.code = code
    }

    // 把图层添加到地图
    this.map.addLayer(layer)
    return layer
  }

  /**
   * 添加点
   * @param layer 添加到的图层 string|layerObj 图层名或图层对象
   * @param lon 经度
   * @param lat 纬度
   * @param option 属性(没有时可直接使用空)[ol.Feature的参数,如果带有geometry将会直接覆盖经纬度点]
   */
  addPoint(layer, lon, lat, option = {}) {
    if (typeof layer === 'string') {
      layer = this.getLayer(layer)
    }
    //如果没有图层对象，直接中断

    if (!layer) {
      return
    }
    let map = this.map
    let coordinatecode = this.option.coordinatecode

    // 经纬度处理
    var defaultOption = {
      geometry: new Point(
        transform(
          [Number(lon), Number(lat)],
          coordinatecode,
          map.getView().getProjection()
        )
      ),
      ...option
    }
    // 添加点
    let feature = new Feature(defaultOption)
    layer.getSource().addFeature(feature)
    return feature
  }

  /**
   * 创建一个点
   * @param lon 经纬度
   * @param lat
   * @param option 其他参数
   */
  createPoint(lon, lat, option) {
    let map = this.map
    let coordinatecode = this.option.coordinatecode
    var defaultOption = {
      geometry: new Point(
        transform(
          [Number(lon), Number(lat)],
          coordinatecode,
          map.getView().getProjection()
        )
      ),
      ...option
    }
    let feature = new Feature(defaultOption)
    return feature
  }

  /**
   * 把地图点数据集合，添加到图层地图显示。
   * @param layerName 图层名称
   * @param iconArr   地图点数据集合
   */
  addIconArrFeatures(layerName, iconArr, isClear = false) {
    var layer =
      typeof layerName === 'object' ? layerName : this.getLayer(layerName)
    //如果没有图层直接创建一个新图层
    if (!layer) {
      layer = this.createIconVectorLayer(layerName)
    }
    let source = layer.getSource()
    if (isClear) {
      source.clear()
    }
    source.addFeatures(iconArr)
    return layer
  }

  // 经纬度转换
  transform(longitude, latitude, mapIns) {
    let map = mapIns || this.map
    let coordinatecode = this.option.coordinatecode
    return transform(
      [Number(longitude), Number(latitude)],
      coordinatecode,
      map.getView().getProjection()
    ) //
  }

  //坐标反转
  toLonLat(longitude, latitude) {
    let coordinatecode = this.option.coordinatecode
    return transform(
      [Number(longitude), Number(latitude)],
      this.map.getView().getProjection(),
      coordinatecode
    ) //
  }

  // 地图图层切换isVisible: true 显示，false隐藏
  mapToggleLayer(isVisible, layername) {
    let layer = this.getLayer(layername)
    if (layer) {
      layer.setVisible(isVisible)
    }
  }

  //创建一个新的带icon的矢量图层
  createIconVectorLayer(name) {
    return this.createVector(
      {
        style: function (features) {
          return new Style({
            image: new Icon({
              src: features.get('src')
            })
          })
        }
      },
      name
    )
  }

  //创建一个弹出层
  createOverlay(container, options) {
    return new Overlay({
      element: container, //绑定 Overlay 对象和 DOM 对象的
      autoPan: true, // 定义弹出窗口在边缘点击时候可能不完整 设置自动平移效果
      positioning: 'bottom-center', //控制弹框的点
      offset: [0, -15],
      autoPanAnimation: {
        duration: 250 //自动平移效果的动画时间 9毫秒
      },
      ...options
    })
  }

  openOverLay(id, coordinate) {
    let overLay = this.map.getOverlayById(id)
    if (overLay) {
      overLay.setPosition(coordinate)
    }
    return overLay
  }

  //关闭一个弹出层
  closeOverLay(id) {
    let overLay = this.map.getOverlayById(id)
    if (overLay) {
      overLay.setPosition(undefined)
    }
    return overLay
  }

  /**
   * 添加一个多多边形
   * @param {*} layer 图层对象
   * @param {*} coordinates  多边形矩阵 数组 [[103.250,92.68],[103.250,92.68],[103.250,92.68]]
   * @param option
   */
  addMultiPolygon(layer, coordinates, option) {
    let coordinatecode = this.option.coordinatecode

    let coord = [[coordinates]]
    let feature = new Feature({
      geometry: new MultiPolygon(coord).transform(
        coordinatecode,
        this.map.getView().getProjection()
      ),
      ...option
    })
    if (layer) {
      layer.getSource().addFeature(feature)
    }
    return feature
  }

  /**
   * 添加多边形
   * @param {*} layer 图层对象
   * @param {*} coordinates  多边形矩阵 数组 [[103.250,92.68],[103.250,92.68],[103.250,92.68]]
   * @param option
   */
  addPolygon(layer, coordinates, option) {
    let coordinatecode = this.option.coordinatecode
    let coord = [coordinates]
    let feature = new Feature({
      geometry: new Polygon(coord),
      ...option
    })
    if (layer) {
      layer.getSource().addFeature(feature)
    }
    return feature
  }

  //添加一条线
  addLineString(layer, coordinates, option) {
    let coordinatecode = this.option.coordinatecode

    let coord = coordinates
    let feature = new Feature({
      geometry: new LineString(coord).transform(
        coordinatecode,
        this.map.getView().getProjection()
      ),
      ...option
    })

    if (layer) {
      layer.getSource().addFeature(feature)
    }
    return feature
  }

  //添加一个圆
  addCircle(layer, params, option) {
    let coordinatecode = this.option.coordinatecode
    let coord = params.center
    let feature = new Feature({
      geometry: new Circle(coord, params.radius).transform(
        coordinatecode,
        this.map.getView().getProjection()
      ),
      ...option
    })
    if (layer) {
      layer.getSource().addFeature(feature)
    }
    return feature
  }

  setViewCenter(center) {
    const view = this.map.getView()
    let coordinatecode = this.option.coordinatecode
    view.setCenter(
      transform(center, coordinatecode, this.map.getView().getProjection())
    )
  }

  /**
   * 视野自适应(定位视图)
   * https://openlayers.vip/apidoc/module-ol_View-View.html#fit
   * @param coordinates
   * @param options
   * @param options.size
   * @param options.padding
   * @param options.nearest
   * @param options.minResolution
   * @param options.maxZoom
   * @param options.duration
   * @param options.easing
   * @param {Function} options.callback
   */
  viewExtent(coordinates = [], options = {}) {
    let extent = new BoundingExtent(coordinates);
    this.map.getView().fit(extent, {
      duration: 300,
      padding: [50, 50, 50, 50],
      // maxZoom: zoom || this.map.getView().getZoom() - 0.1
    })
  }

  // mi转角度（用于显示圆形）
  meterToRadiusDegrees(len) {
    let unit = olProjGet('EPSG:4326').getMetersPerUnit()
    return len / unit
  }

  getBaiduCustomimage(customid) {
    var projection = olProjGet('EPSG:3857')
    var resolutions = []
    for (var i = 0; i < 19; i++) {
      resolutions[i] = Math.pow(2, 18 - i)
    }
    var tilegrid = new TileGrid({
      origin: [0, 0],
      resolutions: resolutions
    })

    var baidu_source = new TileImage({
      projection: projection,
      tileGrid: tilegrid,
      tileUrlFunction: function (tileCoord, pixelRatio, proj) {
        if (!tileCoord) {
          return ''
        }
        var z = tileCoord[0]
        var x = tileCoord[1]

        var y = -tileCoord[2] - 1
        if (x < 0) {
          x = 'M' + -x
        }
        if (y < 0) {
          y = 'M' + -y
        }

        let mg =
          `http://api0.map.bdimg.com/customimage/tile?&x=${x}&y=${y}&z=${z}&udt=20191205&scale=1&ak=5ieMMexWmzB9jivTq6oCRX9j&customid=` +
          customid
        let pl =
          'http://online3.map.bdimg.com/onlinelabel/?qt=tile&x=' +
          x +
          '&y=' +
          y +
          '&z=' +
          z +
          '&styles=pl&udt=20151021&scaler=1&p=1&customid=' +
          customid
        return mg
      }
    })
    return new TileLayer({
      source: baidu_source
    })
  }

  boundingExtent(coordinates) {
    return new BoundingExtent(coordinates)
  }
}

/**
 * 经纬度显示
 * @param length 保留位数
 * @returns {ol.control.MousePosition}
 */
function getMousePosition(length, map) {
  // 创建显示元素
  var div = document.createElement('div')
  div.id = 'mouse-position'
  div.className = 'mouse-position-wrapper'
  var div2 = document.createElement('div')
  div2.className = 'custom-mouse-position'
  div.appendChild(div2)
  map.getTarget().appendChild(div)
  return new MousePosition({
    coordinateFormat: createStringXY(length),
    projection: 'EPSG:4326',
    className: 'custom-mouse-position',
    target: div
  })
}

export default Dbmap
